baubs git
Commit 711f291eff7fea5fde5c5c20c0bd06714e093a20
Parents : a05d199
Author : Jan-Henrik Bruhn <hi@jhbruhn.de>
Date : 2025-12-06T23:20:01+01:00
Add immediate loading states for Upload and Delete operations
Fix UI delay issue where buttons remained clickable during BLE communication
setup. Now provides instant visual feedback when operations begin.
Changes:
- Add isUploading and isDeleting state tracking in useBrotherMachine hook
- Set loading states immediately on button click, before BLE communication
- Update FileUpload component to disable upload button and show spinner
- Update ProgressMonitor to disable delete button with loading indicator
- Add PatternCacheService.deletePattern() method for proper cache cleanup
- Pass loading state props through App.tsx to child components
The UI now responds immediately with disabled buttons and loading spinners,
preventing double-clicks and providing clear feedback during the initial
BLE setup period before progress reporting begins.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes
7 files changed, 51 insertions(+), 1536 deletions(-)
Diff
diff --git a/COMPONENT_IMPLEMENTATION_CHECKLIST.md b/COMPONENT_IMPLEMENTATION_CHECKLIST.md
deleted file mode 100644
index 74c067f..0000000
--- a/COMPONENT_IMPLEMENTATION_CHECKLIST.md
+++ /dev/null
@@ -1,856 +0,0 @@
-# Component Implementation Checklist
-
-Step-by-step guide to implementing UI improvements in each component.
-
----
-
-## Setup (Do First)
-
-### 1. Create Design Tokens
-
-**File: `C:\Users\micro\Documents\dev\respira-web\src\styles\design-tokens.css`**
-
-```css
-@theme {
- /* Primary - Blue */
- --color-primary: #2563eb;
- --color-primary-light: #3b82f6;
- --color-primary-dark: #1d4ed8;
-
- /* Secondary - Slate */
- --color-secondary: #64748b;
- --color-secondary-light: #94a3b8;
-
- /* Success - Green */
- --color-success: #16a34a;
- --color-success-bg: #dcfce7;
-
- /* Warning - Amber */
- --color-warning: #d97706;
- --color-warning-bg: #fef3c7;
-
- /* Danger - Red */
- --color-danger: #dc2626;
- --color-danger-bg: #fee2e2;
-
- /* Info - Cyan */
- --color-info: #0891b2;
- --color-info-bg: #cffafe;
-
- /* Neutral */
- --color-neutral-50: #f9fafb;
- --color-neutral-100: #f3f4f6;
- --color-neutral-200: #e5e7eb;
- --color-neutral-300: #d1d5db;
- --color-neutral-600: #4b5563;
- --color-neutral-900: #111827;
-}
-```
-
-- [ ] Create file
-- [ ] Add all color tokens
-
-### 2. Update App.css
-
-**File: `C:\Users\micro\Documents\dev\respira-web\src\App.css`**
-
-```css
-@import "tailwindcss";
-@import "./styles/design-tokens.css";
-
-/* Custom animations for shimmer effect */
-@keyframes shimmer {
- 0% {
- transform: translateX(-100%);
- }
- 100% {
- transform: translateX(100%);
- }
-}
-```
-
-- [ ] Import design tokens
-- [ ] Verify shimmer animation exists
-
----
-
-## Component Updates
-
-### App.tsx
-
-**File: `C:\Users\micro\Documents\dev\respira-web\src\App.tsx`**
-
-#### Changes:
-1. Header background gradient
-2. Grid gap spacing
-3. Error message styling
-
-**Line 90 - Header gradient:**
-```tsx
-// BEFORE
-<header className="bg-gradient-to-r from-blue-600 to-blue-700 px-8 py-3 shadow-lg">
-
-// AFTER
-<header className="bg-gradient-to-r from-primary to-primary-dark px-8 py-3 shadow-lg">
-```
-
-**Line 111-118 - Error messages:**
-```tsx
-// BEFORE
-<div className="bg-red-100 text-red-900 px-6 py-4 rounded-lg border-l-4 border-red-600 mb-6 shadow-md">
-
-// AFTER
-<div className="bg-danger-bg text-red-900 px-6 py-4 rounded-lg border-l-4 border-danger mb-6 shadow-md">
- <strong>Error:</strong> {machine.error}
-</div>
-```
-
-**Line 120-123 - Info message:**
-```tsx
-// BEFORE
-<div className="bg-blue-100 text-blue-900 px-6 py-4 rounded-lg border-l-4 border-blue-600 mb-6 shadow-md">
-
-// AFTER
-<div className="bg-info-bg text-cyan-900 px-6 py-4 rounded-lg border-l-4 border-info mb-6 shadow-md">
- Initializing Python environment...
-</div>
-```
-
-**Line 126 - Grid gap:**
-```tsx
-// BEFORE
-<div className="grid grid-cols-1 lg:grid-cols-[400px_1fr] gap-6">
-
-// AFTER
-<div className="grid grid-cols-1 lg:grid-cols-[400px_1fr] gap-8">
-```
-
-- [ ] Update header background
-- [ ] Update error styling
-- [ ] Update info message styling
-- [ ] Increase grid gap
-- [ ] Test responsive layout
-
----
-
-### WorkflowStepper.tsx
-
-**File: `C:\Users\micro\Documents\dev\respira-web\src\components\WorkflowStepper.tsx`**
-
-#### Changes:
-1. Progress line thickness
-2. Step circle size
-3. Text size and contrast
-4. Semantic colors
-
-**Line 64-73 - Progress lines:**
-```tsx
-// BEFORE
-<div className="absolute top-4 left-0 right-0 h-0.5 bg-blue-400/30" ... />
-<div className="absolute top-4 left-0 h-0.5 bg-blue-100 ..." ... />
-
-// AFTER
-<div className="absolute top-5 left-0 right-0 h-1 bg-blue-900/20" style={{ left: '20px', right: '20px' }} />
-<div className="absolute top-5 left-0 h-1 bg-blue-100 transition-all duration-500"
- style={{
- left: '20px',
- width: `calc(${((currentStep - 1) / (steps.length - 1)) * 100}% - 20px)`
- }}
-/>
-```
-
-**Line 85-98 - Step circles:**
-```tsx
-// BEFORE
-<div className={`
- w-8 h-8 rounded-full flex items-center justify-center font-bold text-xs transition-all duration-300 border-2
- ${isComplete ? 'bg-green-500 border-green-500 text-white' : ''}
- ${isCurrent ? 'bg-blue-600 border-blue-600 text-white scale-110' : ''}
- ${isUpcoming ? 'bg-blue-700 border-blue-400/30 text-blue-200' : ''}
-`}>
-
-// AFTER
-<div className={`
- w-10 h-10 rounded-full flex items-center justify-center font-bold text-sm transition-all duration-300 border-2
- ${isComplete ? 'bg-success border-success text-white' : ''}
- ${isCurrent ? 'bg-primary border-primary text-white scale-110 shadow-lg shadow-primary/30' : ''}
- ${isUpcoming ? 'bg-blue-700 border-blue-400/30 text-blue-100' : ''}
-`}>
- {isComplete ? (
- <CheckCircleIcon className="w-6 h-6" /> {/* was w-5 h-5 */}
- ) : (
- step.id
- )}
-</div>
-```
-
-**Line 101-105 - Step labels:**
-```tsx
-// BEFORE
-<div className={`text-xs font-semibold ${isCurrent ? 'text-white' : isComplete ? 'text-blue-100' : 'text-blue-300'}`}>
-
-// AFTER
-<div className={`text-sm font-bold tracking-wide ${isCurrent ? 'text-white' : isComplete ? 'text-blue-50' : 'text-blue-200'}`}>
- {step.label}
-</div>
-```
-
-- [ ] Update progress line (h-1, top-5)
-- [ ] Increase circle size (w-10 h-10)
-- [ ] Use semantic colors (success, primary)
-- [ ] Increase text size (text-sm)
-- [ ] Improve contrast (blue-100 vs blue-300)
-- [ ] Add shadow to current step
-- [ ] Enlarge check icon
-
----
-
-### MachineConnection.tsx
-
-**File: `C:\Users\micro\Documents\dev\respira-web\src\components\MachineConnection.tsx`**
-
-#### Changes:
-1. Button styling (Connect, Disconnect)
-2. Status badge border
-3. Auto-refresh indicator
-4. Error/info boxes
-5. Semantic colors
-
-**Line 78-81 - Connect button:**
-```tsx
-// BEFORE
-<button onClick={onConnect} className="px-6 py-3 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3] cursor-pointer">
-
-// AFTER
-<button onClick={onConnect} className="
- px-6 py-3
- bg-primary text-white
- rounded-lg font-semibold text-sm
- shadow-sm
- hover:bg-primary-light hover:shadow-md hover:scale-[1.02]
- active:bg-primary-dark active:scale-[0.98]
- focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2
- disabled:bg-neutral-300 disabled:text-neutral-500 disabled:cursor-not-allowed disabled:shadow-none
- transition-all duration-150 ease-in-out
- cursor-pointer
-" aria-label="Connect to Brother embroidery machine via Bluetooth">
- Connect to Machine
-</button>
-```
-
-**Line 68-73 - Auto-refresh indicator:**
-```tsx
-// BEFORE
-<span className="flex items-center gap-2 text-xs text-gray-500">
- <span className="w-2 h-2 bg-blue-500 rounded-full animate-pulse"></span>
- Auto-refreshing
-</span>
-
-// AFTER
-<span className="flex items-center gap-2 px-3 py-1.5 bg-blue-50 rounded-full text-xs font-medium text-blue-700 border border-blue-200">
- <span className="w-2 h-2 bg-blue-500 rounded-full animate-pulse"></span>
- Auto-refreshing
-</span>
-```
-
-**Line 116-120 - Status badge:**
-```tsx
-// BEFORE
-<span className={`flex items-center gap-2 px-3 py-1.5 rounded-lg font-semibold text-sm ${statusBadgeColors[stateVisual.color]}`}>
-
-// AFTER
-<span className={`
- inline-flex items-center gap-2
- px-3.5 py-2
- rounded-lg
- font-semibold text-sm
- border border-current/20
- ${statusBadgeColors[stateVisual.color]}
-`}>
- <span className="text-base leading-none">{stateVisual.icon}</span>
- <span>{machineStatusName}</span>
-</span>
-```
-
-**Line 141-144 - Disconnect button:**
-```tsx
-// BEFORE
-<button onClick={handleDisconnectClick} className="w-full px-6 py-3 bg-red-600 text-white rounded font-semibold text-sm hover:bg-red-700 transition-all hover:shadow-md cursor-pointer">
-
-// AFTER
-<button onClick={handleDisconnectClick} className="
- w-full px-6 py-3
- bg-danger text-white
- rounded-lg font-semibold text-sm
- shadow-sm
- hover:bg-red-700 hover:shadow-md hover:scale-[1.02]
- active:bg-red-800 active:scale-[0.98]
- focus:outline-none focus:ring-2 focus:ring-danger focus:ring-offset-2
- transition-all duration-150
- cursor-pointer
-">
- Disconnect
-</button>
-```
-
-**Line 88-95 - Informational message:**
-```tsx
-// BEFORE
-<div className="mb-4 p-4 bg-blue-50 border border-blue-200 rounded-lg">
-
-// AFTER
-<div className="mb-4 p-4 bg-info-bg border border-cyan-200 rounded-lg">
-```
-
-- [ ] Update Connect button (primary colors, states)
-- [ ] Update Disconnect button (danger color, states)
-- [ ] Add border to status badge
-- [ ] Enhance auto-refresh indicator
-- [ ] Use semantic colors throughout
-- [ ] Add ARIA labels
-- [ ] Remove grayscale filters
-
----
-
-### FileUpload.tsx
-
-**File: `C:\Users\micro\Documents\dev\respira-web\src\components\FileUpload.tsx`**
-
-#### Changes:
-1. Choose File button styling
-2. Upload button styling
-3. Pattern information grid
-4. Progress bar
-
-**Line 106-108 - Choose File label:**
-```tsx
-// BEFORE
-<label htmlFor="file-input" className={`inline-block px-6 py-3 bg-gray-600 text-white rounded font-semibold text-sm transition-all ${!pyodideReady || isLoading || patternUploaded ? 'opacity-50 cursor-not-allowed grayscale-[0.3]' : 'cursor-pointer hover:bg-gray-700 hover:shadow-md'}`}>
-
-// AFTER
-<label htmlFor="file-input" className={`
- inline-block px-6 py-3
- ${!pyodideReady || isLoading || patternUploaded ? 'bg-neutral-300 text-neutral-500 cursor-not-allowed' : 'bg-secondary text-white hover:bg-secondary-light hover:shadow-md cursor-pointer'}
- rounded-lg font-semibold text-sm
- shadow-sm
- transition-all duration-150
-`}>
- {isLoading ? 'Loading...' : !pyodideReady ? 'Initializing...' : patternUploaded ? 'Pattern Locked' : 'Choose PES File'}
-</label>
-```
-
-**Line 113-133 - Pattern information (replace with grid):**
-```tsx
-// BEFORE
-<div className="bg-gray-50 p-4 rounded-lg space-y-3">
- <div className="flex justify-between">
- <span className="font-medium text-gray-700">File Name:</span>
- <span className="font-semibold text-gray-900">{displayFileName}</span>
- </div>
- {/* ... more rows ... */}
-</div>
-
-// AFTER
-<div className="grid grid-cols-2 gap-3">
- <div className="bg-white p-3 rounded-lg border border-neutral-200 shadow-sm">
- <div className="text-xs font-medium text-neutral-500 mb-1.5 uppercase tracking-wide">File Name</div>
- <div className="font-semibold text-sm text-neutral-900 truncate" title={displayFileName}>
- {displayFileName}
- </div>
- </div>
- <div className="bg-white p-3 rounded-lg border border-neutral-200 shadow-sm">
- <div className="text-xs font-medium text-neutral-500 mb-1.5 uppercase tracking-wide">Pattern Size</div>
- <div className="font-semibold text-sm text-neutral-900">
- {((pesData.bounds.maxX - pesData.bounds.minX) / 10).toFixed(1)} x{' '}
- {((pesData.bounds.maxY - pesData.bounds.minY) / 10).toFixed(1)} mm
- </div>
- </div>
- <div className="bg-white p-3 rounded-lg border border-neutral-200 shadow-sm">
- <div className="text-xs font-medium text-neutral-500 mb-1.5 uppercase tracking-wide">Thread Colors</div>
- <div className="font-semibold text-sm text-neutral-900">{pesData.colorCount}</div>
- </div>
- <div className="bg-white p-3 rounded-lg border border-neutral-200 shadow-sm">
- <div className="text-xs font-medium text-neutral-500 mb-1.5 uppercase tracking-wide">Total Stitches</div>
- <div className="font-semibold text-sm text-neutral-900">{pesData.stitchCount.toLocaleString()}</div>
- </div>
-</div>
-```
-
-**Line 138-146 - Upload button:**
-```tsx
-// BEFORE
-<button onClick={handleUpload} disabled={!isConnected || uploadProgress > 0} className="mt-4 px-6 py-3 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3] cursor-pointer">
-
-// AFTER
-<button onClick={handleUpload} disabled={!isConnected || uploadProgress > 0} className="
- mt-4 px-6 py-3
- bg-primary text-white
- rounded-lg font-semibold text-sm
- shadow-sm
- hover:bg-primary-light hover:shadow-md
- active:bg-primary-dark active:scale-[0.98]
- focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2
- disabled:bg-neutral-300 disabled:text-neutral-500 disabled:cursor-not-allowed disabled:shadow-none
- transition-all duration-150
- cursor-pointer
-">
- {uploadProgress > 0 ? `Uploading... ${uploadProgress.toFixed(0)}%` : 'Upload to Machine'}
-</button>
-```
-
-**Line 155-161 - Progress bar:**
-```tsx
-// BEFORE
-<div className="h-3 bg-gray-300 rounded-md overflow-hidden my-4 shadow-inner">
- <div className="h-full bg-gradient-to-r from-blue-600 to-blue-700 ..." style={{ width: `${uploadProgress}%` }} />
-</div>
-
-// AFTER
-<div className="h-2.5 bg-neutral-200 rounded-full overflow-hidden my-4 shadow-inner">
- <div
- className="h-full bg-gradient-to-r from-primary to-primary-light transition-all duration-300 ease-out relative overflow-hidden"
- style={{ width: `${uploadProgress}%` }}
- role="progressbar"
- aria-valuenow={uploadProgress}
- aria-valuemin={0}
- aria-valuemax={100}
- aria-label="Upload progress"
- >
- <div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/30 to-transparent animate-[shimmer_2s_infinite]" />
- </div>
-</div>
-```
-
-- [ ] Update Choose File button
-- [ ] Replace pattern info with grid layout
-- [ ] Update Upload button
-- [ ] Enhance progress bar
-- [ ] Use semantic colors
-- [ ] Add ARIA attributes
-- [ ] Remove grayscale filter
-
----
-
-### ProgressMonitor.tsx
-
-**File: `C:\Users\micro\Documents\dev\respira-web\src\components\ProgressMonitor.tsx`**
-
-#### Changes:
-1. Grid gap
-2. Color block design
-3. Button styling
-4. Progress bar
-5. Semantic colors
-
-**Line 116 - Grid gap:**
-```tsx
-// BEFORE
-<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
-
-// AFTER
-<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
-```
-
-**Line 143 - Color block container gap:**
-```tsx
-// BEFORE
-<div className="flex flex-col gap-2">
-
-// AFTER
-<div className="flex flex-col gap-3">
-```
-
-**Line 157-191 - Color blocks (major update):**
-```tsx
-// BEFORE
-<div className={`p-2 rounded bg-gray-100 border-2 border-transparent transition-all ${
- isCompleted ? 'border-green-600 bg-green-50' : isCurrent ? 'border-blue-600 bg-blue-50 shadow-md shadow-blue-600/20' : 'opacity-60'
-}`}>
-
-// AFTER
-<div className={`
- p-3 rounded-lg border-2 transition-all duration-200
- ${isCompleted ? 'border-success bg-success-bg/50 shadow-sm' : ''}
- ${isCurrent ? 'border-primary bg-primary/5 shadow-lg shadow-primary/10 ring-2 ring-primary/20 scale-[1.02]' : ''}
- ${!isCompleted && !isCurrent ? 'border-neutral-200 bg-neutral-50 opacity-70' : ''}
-`}>
- <div className="flex items-center gap-3">
- <div
- className="w-6 h-6 rounded-md border-2 border-white shadow-md ring-1 ring-neutral-300 flex-shrink-0"
- style={{ backgroundColor: block.threadHex }}
- aria-label={`Thread color ${block.threadHex}`}
- />
- <span className="font-semibold text-sm flex-1">
- Thread {block.colorIndex + 1}
- </span>
- {isCompleted && <CheckCircleIcon className="w-5 h-5 text-success" aria-label="Completed" />}
- {isCurrent && <ArrowRightIcon className="w-5 h-5 text-primary" aria-label="Current thread" />}
- {!isCompleted && !isCurrent && <CircleStackIcon className="w-5 h-5 text-neutral-400" aria-label="Upcoming" />}
- <span className="text-sm text-neutral-600 font-medium">
- {block.stitchCount.toLocaleString()}
- </span>
- </div>
- {isCurrent && (
- <div className="mt-2.5 h-1.5 bg-neutral-200 rounded-full overflow-hidden">
- <div
- className="h-full bg-primary transition-all duration-300 ease-out"
- style={{ width: `${blockProgress}%` }}
- role="progressbar"
- aria-valuenow={blockProgress}
- aria-valuemin={0}
- aria-valuemax={100}
- />
- </div>
- )}
-</div>
-```
-
-**Line 207-209 - Sewing progress bar:**
-```tsx
-// BEFORE
-<div className="h-3 bg-gray-300 rounded-md overflow-hidden shadow-inner relative mb-2">
- <div className="h-full bg-gradient-to-r from-blue-600 to-blue-700 ..." />
-</div>
-
-// AFTER
-<div className="h-2.5 bg-neutral-200 rounded-full overflow-hidden shadow-inner mb-2">
- <div
- className="h-full bg-gradient-to-r from-primary to-primary-light transition-all duration-300 ease-out relative overflow-hidden"
- style={{ width: `${progressPercent}%` }}
- role="progressbar"
- aria-valuenow={progressPercent}
- aria-valuemin={0}
- aria-valuemax={100}
- aria-label="Sewing progress"
- >
- <div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/30 to-transparent animate-[shimmer_2s_infinite]" />
- </div>
-</div>
-```
-
-**Line 256-259 - Resume button:**
-```tsx
-// BEFORE
-<button onClick={onResumeSewing} className="flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md cursor-pointer">
-
-// AFTER
-<button onClick={onResumeSewing} className="
- flex items-center gap-2
- px-4 py-2
- bg-primary text-white
- rounded-lg font-semibold text-sm
- shadow-sm
- hover:bg-primary-light hover:shadow-md
- active:bg-primary-dark active:scale-[0.98]
- focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2
- transition-all duration-150
- cursor-pointer
-">
- <PlayIcon className="w-4 h-4" />
- Resume Sewing
-</button>
-```
-
-**Line 264-267 - Start Sewing button:**
-```tsx
-// Similar update as Resume button
-```
-
-**Line 271-274 - Start Mask Trace button:**
-```tsx
-// BEFORE
-<button onClick={onStartMaskTrace} className="px-4 py-2 bg-gray-600 text-white rounded font-semibold text-sm hover:bg-gray-700 ...">
-
-// AFTER
-<button onClick={onStartMaskTrace} className="
- px-4 py-2
- bg-secondary text-white
- rounded-lg font-semibold text-sm
- shadow-sm
- hover:bg-secondary-light hover:shadow-md
- active:scale-[0.98]
- focus:outline-none focus:ring-2 focus:ring-secondary focus:ring-offset-2
- transition-all duration-150
- cursor-pointer
-">
- {isMaskTraceComplete ? 'Trace Again' : 'Start Mask Trace'}
-</button>
-```
-
-**Line 278-281 - Delete button:**
-```tsx
-// BEFORE
-<button onClick={onDeletePattern} className="px-4 py-2 bg-red-600 text-white rounded font-semibold text-sm hover:bg-red-700 ...">
-
-// AFTER
-<button onClick={onDeletePattern} className="
- px-4 py-2
- bg-danger text-white
- rounded-lg font-semibold text-sm
- shadow-sm
- hover:bg-red-700 hover:shadow-md
- active:bg-red-800 active:scale-[0.98]
- focus:outline-none focus:ring-2 focus:ring-danger focus:ring-offset-2
- transition-all duration-150
- ml-auto
- cursor-pointer
-">
- Delete Pattern
-</button>
-```
-
-- [ ] Increase grid gap
-- [ ] Update color block design (larger swatches, better states)
-- [ ] Add ARIA labels to color blocks
-- [ ] Enhance progress bar with shimmer
-- [ ] Update all button styles
-- [ ] Use semantic colors
-- [ ] Remove grayscale filters
-
----
-
-### PatternCanvas.tsx
-
-**File: `C:\Users\micro\Documents\dev\respira-web\src\components\PatternCanvas.tsx`**
-
-#### Changes:
-1. Overlay styling unification
-2. Zoom button sizing
-3. Thread legend improvement
-
-**Line 270-281 - Thread Legend:**
-```tsx
-// BEFORE
-<div className="absolute top-2.5 left-2.5 bg-white/95 backdrop-blur-sm p-3 rounded-lg shadow-lg z-10 max-w-[150px]">
- <h4 className="m-0 mb-2 text-[13px] font-semibold text-gray-900 border-b border-gray-300 pb-1.5">Threads</h4>
-
-// AFTER
-<div className="absolute top-3 left-3 bg-white/98 backdrop-blur-sm p-4 rounded-xl shadow-lg border border-neutral-200 z-10 max-w-[180px]">
- <h4 className="text-sm font-bold text-neutral-900 mb-3">Threads</h4>
- {pesData.threads.map((thread, index) => (
- <div key={index} className="flex items-center gap-2.5 mb-2 last:mb-0">
- <div
- className="w-6 h-6 rounded-md border-2 border-white shadow-md ring-1 ring-neutral-300 flex-shrink-0"
- style={{ backgroundColor: thread.hex }}
- />
- <span className="text-sm text-neutral-900 font-medium">Thread {index + 1}</span>
- </div>
- ))}
-</div>
-```
-
-**Line 284-287 - Pattern Dimensions:**
-```tsx
-// BEFORE
-<div className="absolute bottom-[165px] right-5 bg-white/95 backdrop-blur-sm px-4 py-2 rounded-lg shadow-lg z-[11] ...">
-
-// AFTER
-<div className="absolute bottom-[165px] right-5 bg-white/98 backdrop-blur-sm px-4 py-2 rounded-xl shadow-lg border border-neutral-200 z-[11] ...">
-```
-
-**Line 290-298 - Pattern Offset:**
-```tsx
-// BEFORE
-<div className="absolute bottom-20 right-5 bg-white/95 backdrop-blur-sm p-2.5 px-3.5 rounded-lg shadow-lg z-[11] ...">
-
-// AFTER
-<div className="absolute bottom-20 right-5 bg-white/98 backdrop-blur-sm p-3 rounded-xl shadow-lg border border-neutral-200 z-[11] ...">
-```
-
-**Line 301-311 - Zoom Controls:**
-```tsx
-// BEFORE
-<div className="absolute bottom-5 right-5 flex gap-2 items-center bg-white/95 backdrop-blur-sm px-3 py-2 rounded-lg shadow-lg z-10">
- <button className="w-8 h-8 p-1 border border-gray-300 bg-white rounded ...">
-
-// AFTER
-<div className="absolute bottom-5 right-5 flex items-center gap-1 bg-white/98 backdrop-blur-sm px-2 py-2 rounded-xl shadow-lg border border-neutral-200 z-10">
- <button className="
- w-10 h-10
- flex items-center justify-center
- bg-white border border-neutral-300
- rounded-lg
- text-neutral-700
- hover:bg-primary hover:text-white hover:border-primary
- active:bg-primary-dark active:scale-95
- focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2
- transition-all duration-150
- cursor-pointer
- " onClick={handleZoomIn} aria-label="Zoom in on pattern preview" title="Zoom In">
- <PlusIcon className="w-5 h-5" />
- </button>
- <div className="w-px h-6 bg-neutral-200 mx-1"></div>
- <span className="px-3 text-sm font-semibold text-neutral-900 select-none">{Math.round(stageScale * 100)}%</span>
- <div className="w-px h-6 bg-neutral-200 mx-1"></div>
- <button ... aria-label="Zoom out from pattern preview" title="Zoom Out">
- <MinusIcon className="w-5 h-5" />
- </button>
- <button ... aria-label="Reset zoom to fit pattern" title="Reset Zoom">
- <ArrowPathIcon className="w-5 h-5" />
- </button>
-</div>
-```
-
-- [ ] Update all overlays (rounded-xl, border)
-- [ ] Increase zoom button size (w-10 h-10)
-- [ ] Add separators between zoom controls
-- [ ] Larger thread swatches (w-6 h-6)
-- [ ] Add ARIA labels to buttons
-- [ ] Use semantic colors
-- [ ] Improve contrast
-
----
-
-### NextStepGuide.tsx
-
-**File: `C:\Users\micro\Documents\dev\respira-web\src\components\NextStepGuide.tsx`**
-
-#### Changes:
-1. Icon size
-2. Semantic colors
-3. Border styling
-
-**Line 31-53 - Informational messages:**
-```tsx
-// BEFORE
-<div className="bg-blue-50 border-l-4 border-blue-600 p-6 rounded-lg shadow-md">
- <div className="flex items-start gap-4">
- <InformationCircleIcon className="w-8 h-8 text-blue-600 flex-shrink-0 mt-1" />
-
-// AFTER
-<div className="bg-info-bg border-l-4 border-info p-6 rounded-lg shadow-md">
- <div className="flex items-start gap-4">
- <InformationCircleIcon className="w-10 h-10 text-info flex-shrink-0 mt-1" />
-```
-
-**Line 59-87 - Error messages:**
-```tsx
-// BEFORE
-<div className="bg-red-50 border-l-4 border-red-600 p-6 rounded-lg shadow-md">
- <div className="flex items-start gap-4">
- <ExclamationTriangleIcon className="w-8 h-8 text-red-600 flex-shrink-0 mt-1" />
-
-// AFTER
-<div className="bg-danger-bg border-l-4 border-danger p-6 rounded-lg shadow-md">
- <div className="flex items-start gap-4">
- <ExclamationTriangleIcon className="w-10 h-10 text-danger flex-shrink-0 mt-1" />
-```
-
-**Apply similar changes to all state messages (lines 92-299)**
-
-- [ ] Increase icon size (w-10 h-10)
-- [ ] Use semantic colors (info, danger, warning, success)
-- [ ] Ensure consistent styling across all states
-- [ ] Verify border colors match semantic tokens
-
----
-
-## Testing Checklist
-
-After completing all component updates, test:
-
-### Visual Tests
-- [ ] All buttons have consistent rounded-lg corners
-- [ ] All buttons show hover state (color change, shadow)
-- [ ] All buttons show active state (scale down)
-- [ ] Disabled buttons are gray, not grayscale filtered
-- [ ] Colors are consistent (no stray blue-600/700)
-- [ ] Spacing looks balanced
-- [ ] Section headers stand out clearly
-
-### Interaction Tests
-- [ ] Tab through interface - all interactive elements show focus ring
-- [ ] Click buttons - feel responsive with scale feedback
-- [ ] Hover buttons - smooth color transitions
-- [ ] Touch on mobile - 44px touch targets work well
-- [ ] Zoom controls - easy to tap, clear feedback
-
-### Accessibility Tests
-- [ ] Screen reader announces progress bars correctly
-- [ ] Button ARIA labels are present and clear
-- [ ] Color contrast meets WCAG AA (4.5:1 text, 3:1 UI)
-- [ ] Focus states visible on all elements
-- [ ] Status conveyed by more than color alone
-
-### Responsive Tests
-- [ ] Mobile: Single column layout works
-- [ ] Tablet: Grid layout switches correctly
-- [ ] Desktop: Full two-column layout
-- [ ] WorkflowStepper readable on all sizes
-- [ ] Color blocks scroll on mobile
-
-### State Tests
-- [ ] Not connected: NextStepGuide shows step 1
-- [ ] Connected: Components appear correctly
-- [ ] Pattern loaded: Preview displays
-- [ ] Uploading: Progress bar animates with shimmer
-- [ ] Sewing: Color blocks highlight correctly
-- [ ] Complete: Success states show green
-
----
-
-## Estimated Time per Component
-
-| Component | Time | Complexity |
-|-----------|------|------------|
-| Setup (tokens) | 10 min | Low |
-| App.tsx | 10 min | Low |
-| WorkflowStepper | 15 min | Medium |
-| MachineConnection | 20 min | Medium |
-| FileUpload | 25 min | High |
-| ProgressMonitor | 35 min | High |
-| PatternCanvas | 20 min | Medium |
-| NextStepGuide | 15 min | Low |
-| **Total** | **~2.5 hours** | |
-
----
-
-## Quick Win Order
-
-If you have limited time, implement in this order for maximum impact:
-
-1. **Setup + App.tsx** (20 min) - Foundation
-2. **MachineConnection buttons** (10 min) - Most visible
-3. **ProgressMonitor color blocks** (20 min) - High impact
-4. **WorkflowStepper** (15 min) - Always visible when connected
-5. **FileUpload pattern grid** (15 min) - Clear improvement
-6. **PatternCanvas zoom controls** (10 min) - Frequently used
-7. **All other buttons** (15 min) - Consistency
-8. **Polish and test** (20 min) - Final touches
-
-**1.5 hour version**: Do steps 1-4
-**2 hour version**: Do steps 1-6
-**Full version**: All steps
-
----
-
-## Common Pitfalls to Avoid
-
-1. **Don't mix old and new colors** - Replace all instances at once per component
-2. **Don't forget ARIA labels** - Add to all icon buttons
-3. **Don't skip focus states** - Critical for accessibility
-4. **Don't use grayscale filter** - Use neutral-300 background instead
-5. **Don't make text too small** - Minimum 12px (text-xs)
-6. **Don't forget active states** - Add scale-[0.98] to buttons
-7. **Don't skip testing** - Test all states before moving to next component
-
----
-
-## Success Criteria
-
-You'll know the update is successful when:
-
-- [ ] No blue-600/700 classes remain (except as fallbacks)
-- [ ] No grayscale filters on disabled buttons
-- [ ] All buttons have focus:ring-2
-- [ ] All progress bars have shimmer effect
-- [ ] Color blocks visually distinct (completed/current/upcoming)
-- [ ] Touch targets minimum 44px
-- [ ] Consistent rounded-lg on all buttons
-- [ ] Semantic color tokens used throughout
-- [ ] ARIA labels on all icon buttons
-- [ ] Interface feels more polished and professional
-
----
-
-Good luck with the implementation! Refer to `UI_DESIGN_ANALYSIS.md` for detailed rationale and `QUICK_UI_IMPROVEMENTS.md` for condensed code snippets.
diff --git a/DESIGN_TOKENS_REFERENCE.md b/DESIGN_TOKENS_REFERENCE.md
deleted file mode 100644
index 59d99c3..0000000
--- a/DESIGN_TOKENS_REFERENCE.md
+++ /dev/null
@@ -1,671 +0,0 @@
-# Design Tokens Quick Reference
-
-Quick lookup for all design tokens and standard patterns in SKiTCH Controller.
-
----
-
-## Colors
-
-### Semantic Colors
-
-```css
-/* Primary - Blue (Actions, Branding) */
---color-primary: #2563eb; /* blue-600 */
---color-primary-light: #3b82f6; /* blue-500 */
---color-primary-dark: #1d4ed8; /* blue-700 */
-
-/* Secondary - Slate (Secondary actions) */
---color-secondary: #64748b; /* slate-600 */
---color-secondary-light: #94a3b8; /* slate-400 */
-
-/* Success - Green (Complete, Good) */
---color-success: #16a34a; /* green-600 */
---color-success-bg: #dcfce7; /* green-100 */
-
-/* Warning - Amber (Wait, Caution) */
---color-warning: #d97706; /* amber-600 */
---color-warning-bg: #fef3c7; /* amber-100 */
-
-/* Danger - Red (Errors, Destructive) */
---color-danger: #dc2626; /* red-600 */
---color-danger-bg: #fee2e2; /* red-100 */
-
-/* Info - Cyan (Active, Information) */
---color-info: #0891b2; /* cyan-600 */
---color-info-bg: #cffafe; /* cyan-100 */
-
-/* Neutral - Gray (Text, Borders) */
---color-neutral-50: #f9fafb; /* gray-50 - Subtle BG */
---color-neutral-100: #f3f4f6; /* gray-100 - Card BG */
---color-neutral-200: #e5e7eb; /* gray-200 - Borders */
---color-neutral-300: #d1d5db; /* gray-300 - Disabled BG */
---color-neutral-600: #4b5563; /* gray-600 - Secondary text */
---color-neutral-900: #111827; /* gray-900 - Primary text */
-```
-
-### Usage Guide
-
-| Token | Use For | Example |
-|-------|---------|---------|
-| `primary` | Main actions, links, current state | Connect, Upload, Start buttons |
-| `secondary` | Less important actions | Mask Trace, secondary buttons |
-| `success` | Completed states, positive feedback | Sewing complete, upload success |
-| `warning` | Waiting states, cautions | Color change wait, pause |
-| `danger` | Errors, destructive actions | Delete, disconnect, errors |
-| `info` | Active states, information | Sewing in progress, informational messages |
-| `neutral-900` | Primary text | Headings, body text |
-| `neutral-600` | Secondary text | Labels, helper text |
-| `neutral-300` | Borders, disabled states | Card borders, disabled buttons |
-| `neutral-100` | Backgrounds | Card backgrounds |
-| `neutral-50` | Subtle backgrounds | Component backgrounds |
-
----
-
-## Typography
-
-### Text Sizes
-
-```tsx
-// Headings
-h1: "text-2xl font-bold text-neutral-900" // 24px - Main title
-h2: "text-xl font-semibold text-neutral-900" // 20px - Section titles
-h3: "text-base font-semibold text-neutral-900" // 16px - Subsections
-h4: "text-sm font-semibold text-neutral-700" // 14px - Small headings
-
-// Body Text
-text-base: 16px // Large body text, important info
-text-sm: 14px // Standard body text (MINIMUM)
-text-xs: 12px // Small text, captions
-text-[11px]: 11px // Very small (use sparingly)
-
-// Line Heights
-leading-tight: 1.25 // Headings
-leading-normal: 1.5 // Body text
-leading-relaxed: 1.625 // Comfortable reading
-```
-
-### Font Weights
-
-```tsx
-font-normal: 400 // Regular text
-font-medium: 500 // Emphasis
-font-semibold: 600 // Headings, labels
-font-bold: 700 // Main headings, important
-```
-
-### Usage Examples
-
-```tsx
-// Page title
-<h1 className="text-2xl font-bold text-neutral-900">SKiTCH Controller</h1>
-
-// Section header
-<h2 className="text-xl font-semibold text-neutral-900">Pattern Preview</h2>
-
-// Subsection
-<h3 className="text-base font-semibold text-neutral-900">Pattern Information</h3>
-
-// Small heading
-<h4 className="text-sm font-semibold text-neutral-700">Color Blocks</h4>
-
-// Body text
-<p className="text-sm text-neutral-700 leading-normal">Description text...</p>
-
-// Label
-<span className="text-xs font-medium text-neutral-600 uppercase tracking-wide">File Name</span>
-
-// Value
-<span className="text-sm font-semibold text-neutral-900">pattern.pes</span>
-```
-
----
-
-## Spacing
-
-### Padding Scale
-
-```tsx
-p-2: 0.5rem (8px) // Tight
-p-3: 0.75rem (12px) // Compact
-p-4: 1rem (16px) // Standard
-p-5: 1.25rem (20px) // Comfortable
-p-6: 1.5rem (24px) // Spacious
-p-8: 2rem (32px) // Very spacious
-```
-
-### Gap Scale
-
-```tsx
-gap-1: 0.25rem (4px) // Very tight
-gap-2: 0.5rem (8px) // Tight grouping
-gap-3: 0.75rem (12px) // Related items
-gap-4: 1rem (16px) // Section spacing
-gap-6: 1.5rem (24px) // Major sections
-gap-8: 2rem (32px) // Large gaps
-```
-
-### Margin Scale
-
-```tsx
-mb-2: 0.5rem (8px) // Tight
-mb-3: 0.75rem (12px) // Compact
-mb-4: 1rem (16px) // Standard
-mb-5: 1.25rem (20px) // Comfortable
-mb-6: 1.5rem (24px) // Spacious
-mb-8: 2rem (32px) // Large gap
-```
-
-### Standard Component Spacing
-
-```tsx
-// Card padding
-"p-6" // Standard card (24px)
-"p-5" // Compact card (20px)
-"p-4" // Small card (16px)
-
-// Content stacking
-"space-y-2" // Tight grouping (8px)
-"space-y-3" // Related items (12px)
-"space-y-4" // Section spacing (16px)
-"space-y-6" // Major sections (24px)
-
-// Grid gaps
-"gap-3" // Tight grid (12px)
-"gap-4" // Standard grid (16px)
-"gap-6" // Spacious grid (24px)
-```
-
----
-
-## Buttons
-
-### Primary Button (Main Actions)
-
-```tsx
-className="
- px-6 py-3
- bg-primary text-white
- rounded-lg font-semibold text-sm
- shadow-sm
- hover:bg-primary-light hover:shadow-md hover:scale-[1.02]
- active:bg-primary-dark active:scale-[0.98]
- focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2
- disabled:bg-neutral-300 disabled:text-neutral-500 disabled:cursor-not-allowed disabled:shadow-none
- transition-all duration-150 ease-in-out
- cursor-pointer
-"
-```
-
-**Use for:** Connect, Upload, Start Sewing, Resume
-
----
-
-### Secondary Button (Less Important Actions)
-
-```tsx
-className="
- px-6 py-3
- bg-white text-neutral-700 border border-neutral-300
- rounded-lg font-semibold text-sm
- shadow-sm
- hover:bg-neutral-50 hover:border-neutral-400
- active:bg-neutral-100 active:scale-[0.98]
- focus:outline-none focus:ring-2 focus:ring-neutral-400 focus:ring-offset-2
- disabled:bg-neutral-50 disabled:text-neutral-400 disabled:cursor-not-allowed
- transition-all duration-150
- cursor-pointer
-"
-```
-
-**Use for:** Mask Trace, Refresh, Cancel
-
----
-
-### Danger Button (Destructive Actions)
-
-```tsx
-className="
- px-6 py-3
- bg-danger text-white
- rounded-lg font-semibold text-sm
- shadow-sm
- hover:bg-red-700 hover:shadow-md
- active:bg-red-800 active:scale-[0.98]
- focus:outline-none focus:ring-2 focus:ring-danger focus:ring-offset-2
- disabled:bg-neutral-300 disabled:text-neutral-500 disabled:cursor-not-allowed disabled:shadow-none
- transition-all duration-150
- cursor-pointer
-"
-```
-
-**Use for:** Delete, Disconnect
-
----
-
-### Icon Button (Zoom, Controls)
-
-```tsx
-className="
- w-10 h-10
- flex items-center justify-center
- bg-white border border-neutral-300
- rounded-lg
- text-neutral-700
- hover:bg-primary hover:text-white hover:border-primary
- active:bg-primary-dark active:scale-95
- focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2
- transition-all duration-150
- cursor-pointer
-"
-aria-label="Descriptive label"
-```
-
-**Use for:** Zoom in/out/reset, small controls
-
----
-
-## Cards & Containers
-
-### Standard Card
-
-```tsx
-<div className="bg-white p-6 rounded-lg shadow-md border border-neutral-100">
- <div className="mb-5 pb-3 border-b border-neutral-200">
- <h2 className="text-xl font-bold text-neutral-900">Title</h2>
- </div>
- <div className="space-y-4">
- {/* Content */}
- </div>
-</div>
-```
-
----
-
-### Compact Card
-
-```tsx
-<div className="bg-white p-5 rounded-lg shadow-md border border-neutral-100">
- {/* Content */}
-</div>
-```
-
----
-
-### Info Card (Small)
-
-```tsx
-<div className="bg-white p-3 rounded-lg border border-neutral-200 shadow-sm">
- <div className="text-xs font-medium text-neutral-500 mb-1.5 uppercase tracking-wide">
- Label
- </div>
- <div className="font-semibold text-sm text-neutral-900">
- Value
- </div>
-</div>
-```
-
----
-
-## Alert Boxes
-
-### Info Alert
-
-```tsx
-<div className="bg-info-bg border-l-4 border-info p-4 rounded-lg">
- <div className="flex items-start gap-3">
- <InformationCircleIcon className="w-6 h-6 text-info flex-shrink-0" />
- <div>
- <h4 className="font-semibold text-cyan-900 mb-1">Title</h4>
- <p className="text-sm text-cyan-800">Message</p>
- </div>
- </div>
-</div>
-```
-
----
-
-### Success Alert
-
-```tsx
-<div className="bg-success-bg border-l-4 border-success p-4 rounded-lg">
- <div className="flex items-start gap-3">
- <CheckCircleIcon className="w-6 h-6 text-success flex-shrink-0" />
- <div>
- <h4 className="font-semibold text-green-900 mb-1">Title</h4>
- <p className="text-sm text-green-800">Message</p>
- </div>
- </div>
-</div>
-```
-
----
-
-### Warning Alert
-
-```tsx
-<div className="bg-warning-bg border-l-4 border-warning p-4 rounded-lg">
- <div className="flex items-start gap-3">
- <ExclamationTriangleIcon className="w-6 h-6 text-warning flex-shrink-0" />
- <div>
- <h4 className="font-semibold text-amber-900 mb-1">Title</h4>
- <p className="text-sm text-amber-800">Message</p>
- </div>
- </div>
-</div>
-```
-
----
-
-### Error Alert
-
-```tsx
-<div className="bg-danger-bg border-l-4 border-danger p-4 rounded-lg">
- <div className="flex items-start gap-3">
- <XCircleIcon className="w-6 h-6 text-danger flex-shrink-0" />
- <div>
- <h4 className="font-semibold text-red-900 mb-1">Title</h4>
- <p className="text-sm text-red-800">Message</p>
- </div>
- </div>
-</div>
-```
-
----
-
-## Progress Bars
-
-### Standard Progress Bar
-
-```tsx
-<div className="h-2.5 bg-neutral-200 rounded-full overflow-hidden shadow-inner">
- <div
- className="h-full bg-gradient-to-r from-primary to-primary-light transition-all duration-300 ease-out relative overflow-hidden"
- style={{ width: `${progress}%` }}
- role="progressbar"
- aria-valuenow={progress}
- aria-valuemin={0}
- aria-valuemax={100}
- aria-label="Progress description"
- >
- <div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/30 to-transparent animate-[shimmer_2s_infinite]" />
- </div>
-</div>
-```
-
----
-
-### Block Progress Bar (Smaller)
-
-```tsx
-<div className="h-1.5 bg-neutral-200 rounded-full overflow-hidden">
- <div
- className="h-full bg-primary transition-all duration-300"
- style={{ width: `${progress}%` }}
- />
-</div>
-```
-
----
-
-## Status Badges
-
-### Status Badge Template
-
-```tsx
-<span className={`
- inline-flex items-center gap-2
- px-3.5 py-2
- rounded-lg
- font-semibold text-sm
- border border-current/20
- ${statusColor}
-`}>
- <span className="text-base leading-none">{icon}</span>
- <span>{text}</span>
-</span>
-```
-
-### Status Color Classes
-
-```tsx
-const statusColors = {
- idle: 'bg-cyan-100 text-cyan-800 border-cyan-200',
- active: 'bg-yellow-100 text-yellow-800 border-yellow-200',
- complete: 'bg-green-100 text-green-800 border-green-200',
- error: 'bg-red-100 text-red-800 border-red-200',
- info: 'bg-blue-100 text-blue-800 border-blue-200',
-};
-```
-
----
-
-## Overlays (Canvas)
-
-### Standard Overlay
-
-```tsx
-<div className="absolute top-3 left-3 bg-white/98 backdrop-blur-sm p-4 rounded-xl shadow-lg border border-neutral-200 z-10">
- {/* Content */}
-</div>
-```
-
----
-
-## Data Grids
-
-### Info Grid (2×2)
-
-```tsx
-<div className="grid grid-cols-2 gap-3">
- <div className="bg-white p-3 rounded-lg border border-neutral-200 shadow-sm">
- <div className="text-xs font-medium text-neutral-500 mb-1.5 uppercase tracking-wide">
- Label
- </div>
- <div className="font-semibold text-sm text-neutral-900">
- Value
- </div>
- </div>
- {/* Repeat for other cells */}
-</div>
-```
-
----
-
-### Key-Value Rows
-
-```tsx
-<div className="bg-neutral-50 p-4 rounded-lg border border-neutral-200 space-y-3">
- <div className="flex justify-between items-center">
- <span className="text-sm font-medium text-neutral-600">Label:</span>
- <span className="text-base font-bold text-neutral-900">Value</span>
- </div>
- {/* Repeat for other rows */}
-</div>
-```
-
----
-
-## Icons
-
-### Size Scale
-
-```tsx
-w-4 h-4: 16px // Small icons (in buttons)
-w-5 h-5: 20px // Standard icons
-w-6 h-6: 24px // Medium icons (alerts)
-w-8 h-8: 32px // Large icons (status)
-w-10 h-10: 40px // Very large (main features)
-```
-
-### Usage
-
-```tsx
-// In buttons
-<PlayIcon className="w-4 h-4" />
-
-// In alerts
-<InformationCircleIcon className="w-6 h-6 text-info" />
-
-// In headers
-<CheckCircleIcon className="w-8 h-8 text-success" />
-```
-
----
-
-## Border Radius
-
-```tsx
-rounded: 4px // Subtle (rarely used)
-rounded-md: 6px // Small elements
-rounded-lg: 8px // Standard (buttons, cards)
-rounded-xl: 12px // Large elements (overlays)
-rounded-full: 9999px // Pills, circles
-```
-
----
-
-## Shadows
-
-```tsx
-shadow-sm: Small shadow // Buttons, cards
-shadow-md: Medium shadow // Hover states
-shadow-lg: Large shadow // Overlays, modals
-shadow-inner: Inset shadow // Progress bar tracks
-
-// With color
-shadow-primary/10: Colored shadow (10% opacity)
-shadow-primary/20: Stronger colored shadow
-shadow-primary/30: Prominent colored shadow
-```
-
----
-
-## Transitions
-
-```tsx
-// Standard transition
-transition-all duration-150 ease-in-out
-
-// Specific properties
-transition-colors duration-150
-transition-transform duration-150
-transition-opacity duration-300
-
-// Easing
-ease-in-out // Standard
-ease-out // Decelerate
-ease-in // Accelerate
-```
-
----
-
-## Accessibility Attributes
-
-### Buttons
-
-```tsx
-aria-label="Descriptive action" // For icon buttons
-title="Tooltip text" // For hover tooltips
-disabled={condition} // Properly disabled
-```
-
-### Progress Bars
-
-```tsx
-role="progressbar"
-aria-valuenow={currentValue}
-aria-valuemin={0}
-aria-valuemax={100}
-aria-label="What's progressing"
-```
-
-### Interactive Elements
-
-```tsx
-focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2
-tabIndex={0} // If not naturally focusable
-```
-
----
-
-## Touch Targets
-
-### Minimum Sizes
-
-```tsx
-// Buttons: 44×44px minimum
-min-w-[44px] min-h-[44px]
-
-// Icon buttons: Use w-10 h-10 (40px) as minimum
-w-10 h-10 // Close to 44px with padding
-```
-
----
-
-## Responsive Breakpoints
-
-```tsx
-sm: 640px // Small devices
-md: 768px // Tablets
-lg: 1024px // Laptops
-xl: 1280px // Desktops
-2xl: 1536px // Large screens
-```
-
-### Usage
-
-```tsx
-// Mobile-first approach
-<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
-
-// Show/hide by breakpoint
-<div className="hidden md:block">Desktop only</div>
-<div className="md:hidden">Mobile only</div>
-```
-
----
-
-## Copy-Paste Snippets
-
-### Section Header
-```tsx
-<div className="mb-5 pb-3 border-b border-neutral-200">
- <h2 className="text-xl font-bold text-neutral-900">Section Title</h2>
-</div>
-```
-
-### Stat Card
-```tsx
-<div className="bg-white p-3 rounded-lg border border-neutral-200 shadow-sm">
- <div className="text-xs font-medium text-neutral-500 mb-1.5 uppercase tracking-wide">
- Label
- </div>
- <div className="font-semibold text-sm text-neutral-900">
- Value
- </div>
-</div>
-```
-
-### Loading Spinner
-```tsx
-<div className="w-6 h-6 border-2 border-primary border-t-transparent rounded-full animate-spin" />
-```
-
----
-
-## Quick Reference Table
-
-| Element | Padding | Radius | Shadow | Border |
-|---------|---------|--------|--------|--------|
-| **Button** | px-6 py-3 | rounded-lg | shadow-sm | - |
-| **Card** | p-6 | rounded-lg | shadow-md | border-neutral-100 |
-| **Overlay** | p-4 | rounded-xl | shadow-lg | border-neutral-200 |
-| **Alert** | p-4 | rounded-lg | - | border-l-4 |
-| **Badge** | px-3.5 py-2 | rounded-lg | - | border-current/20 |
-| **Stat Card** | p-3 | rounded-lg | shadow-sm | border-neutral-200 |
-
----
-
-**Quick reference for SKiTCH Controller design system**
-**Use with:** All component files
-**Created:** 2025-12-06
diff --git a/src/App.tsx b/src/App.tsx
index 0ab6967..1d2eea4 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -185,6 +185,7 @@ function App() {
resumeAvailable={machine.resumeAvailable}
resumeFileName={machine.resumeFileName}
pesData={pesData}
+ isUploading={machine.isUploading}
/>
)}
</div>
@@ -257,6 +258,7 @@ function App() {
onStartSewing={machine.startSewing}
onResumeSewing={machine.resumeSewing}
onDeletePattern={handleDeletePattern}
+ isDeleting={machine.isDeleting}
/>
)}
</div>
diff --git a/src/components/FileUpload.tsx b/src/components/FileUpload.tsx
index bbb12fa..6718649 100644
--- a/src/components/FileUpload.tsx
+++ b/src/components/FileUpload.tsx
@@ -17,6 +17,7 @@ interface FileUploadProps {
resumeAvailable: boolean;
resumeFileName: string | null;
pesData: PesPatternData | null;
+ isUploading?: boolean;
}
export function FileUpload({
@@ -31,6 +32,7 @@ export function FileUpload({
resumeAvailable,
resumeFileName,
pesData: pesDataProp,
+ isUploading = false,
}: FileUploadProps) {
const [localPesData, setLocalPesData] = useState<PesPatternData | null>(null);
const [fileName, setFileName] = useState<string>('');
@@ -190,17 +192,17 @@ export function FileUpload({
{pesData && canUploadPattern(machineStatus) && !patternUploaded && uploadProgress < 100 && (
<button
onClick={handleUpload}
- disabled={!isConnected || uploadProgress > 0}
+ disabled={!isConnected || isUploading}
className="mt-4 inline-flex items-center gap-2 px-6 py-2.5 bg-blue-600 dark:bg-blue-700 text-white rounded-lg font-semibold text-sm hover:bg-blue-700 dark:hover:bg-blue-600 active:bg-blue-800 dark:active:bg-blue-500 hover:shadow-lg active:scale-[0.98] transition-all duration-150 cursor-pointer focus:outline-none focus:ring-2 focus:ring-blue-300 dark:focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-blue-600 disabled:hover:shadow-none disabled:active:scale-100"
- aria-label={uploadProgress > 0 ? `Uploading pattern: ${uploadProgress.toFixed(0)}% complete` : 'Upload pattern to machine'}
+ aria-label={isUploading ? `Uploading pattern: ${uploadProgress.toFixed(0)}% complete` : 'Upload pattern to machine'}
>
- {uploadProgress > 0 ? (
+ {isUploading ? (
<>
<svg className="w-5 h-5 animate-spin" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
- <span>Uploading... {uploadProgress.toFixed(0)}%</span>
+ <span>Uploading... {uploadProgress > 0 ? uploadProgress.toFixed(0) + '%' : ''}</span>
</>
) : (
<>
@@ -217,11 +219,11 @@ export function FileUpload({
</div>
)}
- {uploadProgress > 0 && uploadProgress < 100 && (
+ {isUploading && uploadProgress < 100 && (
<div className="mt-4 animate-fadeIn">
<div className="flex justify-between items-center mb-2">
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">Uploading to Machine</span>
- <span className="text-sm font-bold text-blue-600 dark:text-blue-400">{uploadProgress.toFixed(1)}%</span>
+ <span className="text-sm font-bold text-blue-600 dark:text-blue-400">{uploadProgress > 0 ? uploadProgress.toFixed(1) + '%' : 'Starting...'}</span>
</div>
<div className="h-3 bg-gray-300 dark:bg-gray-600 rounded-full overflow-hidden shadow-inner relative">
<div
diff --git a/src/components/ProgressMonitor.tsx b/src/components/ProgressMonitor.tsx
index 418d42b..661dad8 100644
--- a/src/components/ProgressMonitor.tsx
+++ b/src/components/ProgressMonitor.tsx
@@ -29,6 +29,7 @@ interface ProgressMonitorProps {
onStartSewing: () => void;
onResumeSewing: () => void;
onDeletePattern: () => void;
+ isDeleting?: boolean;
}
export function ProgressMonitor({
@@ -40,6 +41,7 @@ export function ProgressMonitor({
onStartSewing,
onResumeSewing,
onDeletePattern,
+ isDeleting = false,
}: ProgressMonitorProps) {
// State indicators
const isSewing = machineStatus === MachineStatus.SEWING;
@@ -228,10 +230,21 @@ export function ProgressMonitor({
{patternInfo && canDeletePattern(machineStatus) && (
<button
onClick={onDeletePattern}
- className="px-4 py-2.5 bg-red-600 dark:bg-red-700 text-white rounded-lg font-semibold text-sm hover:bg-red-700 dark:hover:bg-red-600 active:bg-red-800 dark:active:bg-red-500 hover:shadow-lg active:scale-[0.98] transition-all duration-150 cursor-pointer focus:outline-none focus:ring-2 focus:ring-red-300 dark:focus:ring-red-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900 ml-auto"
- aria-label="Delete the current pattern from machine"
+ disabled={isDeleting}
+ className="px-4 py-2.5 bg-red-600 dark:bg-red-700 text-white rounded-lg font-semibold text-sm hover:bg-red-700 dark:hover:bg-red-600 active:bg-red-800 dark:active:bg-red-500 hover:shadow-lg active:scale-[0.98] transition-all duration-150 cursor-pointer focus:outline-none focus:ring-2 focus:ring-red-300 dark:focus:ring-red-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900 ml-auto disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-red-600 disabled:hover:shadow-none disabled:active:scale-100"
+ aria-label={isDeleting ? "Deleting pattern..." : "Delete the current pattern from machine"}
>
- Delete Pattern
+ {isDeleting ? (
+ <>
+ <svg className="w-4 h-4 animate-spin inline mr-2" fill="none" viewBox="0 0 24 24">
+ <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
+ <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
+ </svg>
+ Deleting...
+ </>
+ ) : (
+ 'Delete Pattern'
+ )}
</button>
)}
</div>
diff --git a/src/hooks/useBrotherMachine.ts b/src/hooks/useBrotherMachine.ts
index 9456752..699175c 100644
--- a/src/hooks/useBrotherMachine.ts
+++ b/src/hooks/useBrotherMachine.ts
@@ -28,6 +28,8 @@ export function useBrotherMachine() {
const [uploadProgress, setUploadProgress] = useState<number>(0);
const [error, setError] = useState<string | null>(null);
const [isPolling, setIsPolling] = useState(false);
+ const [isUploading, setIsUploading] = useState(false);
+ const [isDeleting, setIsDeleting] = useState(false);
const [resumeAvailable, setResumeAvailable] = useState(false);
const [resumeFileName, setResumeFileName] = useState<string | null>(null);
const [resumedPattern, setResumedPattern] = useState<{ pesData: PesPatternData; patternOffset?: { x: number; y: number } } | null>(
@@ -203,6 +205,7 @@ export function useBrotherMachine() {
try {
setError(null);
setUploadProgress(0);
+ setIsUploading(true); // Set loading state immediately
const uuid = await service.uploadPattern(
penData,
(progress) => {
@@ -229,6 +232,8 @@ export function useBrotherMachine() {
setError(
err instanceof Error ? err.message : "Failed to upload pattern",
);
+ } finally {
+ setIsUploading(false); // Clear loading state
}
},
[service, isConnected, refreshStatus, refreshPatternInfo],
@@ -277,6 +282,7 @@ export function useBrotherMachine() {
try {
setError(null);
+ setIsDeleting(true); // Set loading state immediately
// Delete pattern from cache to prevent auto-resume
try {
@@ -304,6 +310,8 @@ export function useBrotherMachine() {
await refreshStatus();
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to delete pattern");
+ } finally {
+ setIsDeleting(false); // Clear loading state
}
}, [service, isConnected, refreshStatus]);
@@ -364,6 +372,8 @@ export function useBrotherMachine() {
uploadProgress,
error,
isPolling,
+ isUploading,
+ isDeleting,
resumeAvailable,
resumeFileName,
resumedPattern,
diff --git a/src/services/PatternCacheService.ts b/src/services/PatternCacheService.ts
index bde64af..98b3237 100644
--- a/src/services/PatternCacheService.ts
+++ b/src/services/PatternCacheService.ts
@@ -127,6 +127,21 @@ export class PatternCacheService {
return pattern?.uuid === uuid;
}
+ /**
+ * Delete a specific pattern by UUID
+ */
+ static deletePattern(uuid: string): void {
+ try {
+ const cached = this.getPatternByUUID(uuid);
+ if (cached) {
+ localStorage.removeItem(CACHE_KEY);
+ console.log('[PatternCache] Deleted pattern with UUID:', uuid);
+ }
+ } catch (err) {
+ console.error('[PatternCache] Failed to delete pattern:', err);
+ }
+ }
+
/**
* Clear the pattern cache
*/
Served by rngit 1.4.2 - Generated in 0.15s